home *** CD-ROM | disk | FTP | other *** search
- /*
- * malloc_db.c
- *
- * Malloc heap trashing debugger.
- *
- * Written by Gianni Mariani some time in 1990
- * Updated recently to provide lots of comments.
- *
- * This code may be distributed freely on the following 5 conditions :
- * 1. No money or favor is exchanged for this code.
- * 2. This notice remains part of this code.
- * 3. You do not actively hinder its distribution.
- * 4. Credit is given to the author.
- * 5. Bug fixes or enhancements are passed on based on these
- * conditions.
- *
- * IRIX is a trademark of Silicon Graphics Inc, CA.
- *
- *
- * This module provides all the malloc() memory allocation routines
- * that is provided in the c library. In addition if db_check_on is
- * true, the entire arena is scanned on every free() malloc() or realloc()
- * call. (in dbx, execute "assign db_check_on=1". in cvd, go into the
- * "Expression View" screen from the "Views" pulldown menu, and type
- * "db_check(1)" in the "Expression:" column followed by a carriage return;
- * when db_check(1) is executed successfully (i.e. the memory arena has not
- * yet been trashed and the test performed by "db_check(1)" returned
- * successfully) there will appear something like a 9-digit number in the
- * corresponding "Result:" column that is confirmation that db_check() did
- * not fail.) db_check( 1 ) can also be called from the debugger or other
- * interesting functions to increase the frequency that the arena is
- * checked.
- *
- * db_check( 1 ) checks the arena for writes past the end of allocated
- * memory or before allocated memory, when it finds an inconsistancy it will
- * purposly write to location 0 to core dump. It will set db_bugptr1 thru
- * db_bugptr4 and db_lastgood to interesting pointers. The purpose is so
- * that once the pointer in error is found, one can set a watch point (from
- * within cvd) on it and find the culprit code ! db_bugptr3 and db_bugptr4
- * are usually the addresses that are actually probably corrupted.
- *
- * ->->-> NOTE: YOU MUST "% setenv DB_CHECK ON" TO ACTIVATE IT. <-<-<-
- *
- * Example scenario :
- * 1. app core dumps in malloc(<stripped>) somewhere - bummed.
- * 2. relink with -g3 malloc_db.c and -lmpc
- * 3. run the app.
- * set environment variable
- * -- setenv DB_CHECK ON
- * cross fingers - yes it core dumped in malloc_db.c
- * ( gotcha - I feel like a fosters { fozzie's for ozzies } )
- * -- this bit might take oodles (lots) of time, you might
- * want to get real swift and use the debugger to switch
- * off db_checking by setting db_check_on = 0 and later
- * setting it on db_check_on = 1 when you know you're
- * near the bug.
- * 4. make note of the addresses of db_bugptr4 and db_bugptr3 by:
- * -- looking at stdout in the shell window you ran the app from,
- * -- from within cvd, using the "Expression View" window
- * -- from within dbx, executing "print db_bugptr3,db_bugptr4"
- * 5. set watch points on these addrs
- * These addresses are probably not in the address space yet -
- * this means you have to wait until the address is alloacted by
- * the o.s. (sbrk() usually). To get around this, the first call
- * to db_check( 1 ) will allocate and free DB_CHUNKSIZE of memory
- * ( you might need to adjust DB_CHUNKSIZE ) so that it brings
- * those addresses into the address space of the process.
- * to set a watch point in cvd for db_bugptr3 and db_bugptr4 (call
- * them 0x100d3628 and 0x100d3854 respectively) type in at the
- * "cvd>" prompt: "watch addr 0x100d3628" and on at the next
- * prompt: "watch addr 0x100d3854". by default execution will
- * stop when this address is next written to.
- * 6. restart the app.
- * cross fingers - hopefully cvd will break on one of these 2 watch
- * points. If you get stopped with some kind of message like
- *
- * [1] Process 4406 stopped at ["malloc_db.c":488, 0x400810
- *
- * watch break inside of malloc_db.c, it usually means that
- * malloc_db.c is setting up its own db_malsort memory structs -
- * ignore 'em and execute "continue" to step past the initializing
- * "setup" calls from within malloc_db.c. if it seems like you're
- * having to press "continue" too much, try telling cvd to stop in
- * something else that is related to manipulating memory like read:
- *
- * cvd> stop in read
- * [2] Stop entry read file sys/read.s
- * cvd> rerun
- * [2] Process 4413# stopped at ["read.s":14, 0x4f39c0]
- * cvd> c
- * [2] Process 4413 stopped at ["read.s":14, 0x4f39c0]
- * cvd> status
- * [0] Stop watch address 0x100d3628 size 4 for write access
- * [1] Stop watch address 0x100d3854 size 4 for write access
- * [2] Stop entry read file sys/read.s
- * cvd> c
- * [2] Process 4413 stopped at ["read.s":14, 0x4f39c0]
- * cvd> c
- * [2] Process 4413 stopped at ["read.s":14, 0x4f39c0]
- * cvd> c
- *
- * If it seems that you are having to hit continue too much with
- * "read" as well, do something like:
- *
- * cvd> return
- * Process 4413 stopped at ["XlibInt.c":249, 0x4b2f10]
- * cvd> c
- * [2] Process 4413 stopped at ["read.s":14, 0x4f39c0]
- * cvd> disable 2
- * cvd> status
- * [0] Stop watch address 0x100d3628 size 4 for write access
- * [1] Stop watch address 0x100d3854 size 4 for write access
- * [2] Stop entry read file sys/read.s[inactive]
- * cvd> c
- * Process 4413 stopped at ["malloc_db.c":485, 0x4007f8]
- * cvd> c
- * cvd> return
- *
- * at this point, either you are finding yourself stopping at some
- * sort of "interesting" segment in your own code somewhere, OR you
- * continue to find yourself stopping inside malloc_db.c. if it
- * is the former, run db_check(1) from the "Expression Views" window
- * (to "turn off" db_check() at any point, do something like moving
- * the cursor over the final parenthesis, delete it, and then press
- * carriage return. this will generate a "<syntax error>" in the
- * "Results:" column on the same line where the 9-digit integer used
- * to be--the effect of this is to disable db_check().) if it fails
- * then !!! the culprit code is found !!! (if not you need to
- * continue trying to blindly approaching the invisible segment of
- * your own code where you are mysteriously trashing/walking over
- * memory without your conscious knowledge.)
- * Invoke AI bug fixer.
- * 89689655% AI_bug_fix <file.c> <line num>
- * I'm realeasing AI_bug_fix(1C) some time back to the future !
- * -- just kidding !
- *
- * Notes:
- *
- * If lots of memory elements are allocated it will consume lots of
- * cpu time in db_check().
- *
- * I've found it most helpfull - stomped the yukkiest of critters.
- *
- * Enjoy bug swatting ---!
- *
- * DISCLOSURE: No warranty - specified or otherwise implied is given
- * on the fitness of this code for anything. In other words - you take
- * it - you own the bugs. !
- *
- * You're welcome to send me ( gianni@neu.sgi.com ) email but I might
- * ignore them.
- *
- * Linking - You may need to link it with -lmpc
- *
- * BUGS/RFE's :-
- * 1. there may be up to a three byte gap between the allocated
- * memory and the address that is checked for overwrite. This
- * can be fixed by using a byte by byte comparison routine.
- * 2. The control structures used by amalloc() are not verified
- * so if these are overwritten db_check() will not find it.
- * 3. The real amalloc should provide the information this does
- * when amallopt( M_DEBUG, 1 ) is set.
- * 4. This only works on IRIX systems that have amalloc(3P).
- */
-
- #include <stdio.h>
- #include <malloc.h>
-
- /* ======== db_malsort ================================================ */
- /* PURPOSE:
- * Malloc debugging structure.
- */
-
- typedef struct db_malsort {
- long db_id; /* Debugger ID */
- struct db_malsort * db_links[ 2 ]; /* Links to others */
- long db_rsize; /* requested size of mem */
- long db_size; /* size of mem */
- } db_malsort;
-
- #define DB_IDVAL 0xdf3e55aa
-
- extern db_malsort db_headlink[];
-
- db_malsort db_headlink[ 2 ] = {
- { DB_IDVAL, { db_headlink, db_headlink }, 0, 0 },
- { DB_IDVAL, { db_headlink, db_headlink }, 0, 0 },
- };
-
- int db_check_on = -1; /* Check status flag. */
- char * db_coredump = 0; /* Easy way to core dump */
- db_malsort * db_bugptr1 = 0; /* Pointer that we crap up on */
- db_malsort * db_bugptr2 = 0; /* Pointer that we crap up on */
- long * db_bugptr3 = 0; /* Pointer that we crap up on */
- long * db_bugptr4 = 0; /* Pointer that we crap up on */
-
- db_malsort * db_lastgood = 0; /* Pointer that last checked out */
-
- unsigned long db_sizeit = ~0;
-
- int dbm_extend_size = 0x10000;
-
- extern void * sbrk();
-
- void * dbm_arena;
-
- #define DB_ALIGN( osize ) ( ( ( osize - 1 ) | 3 ) + 1 )
-
- /* ======== dbm_getarena ============================================== */
- /* PURPOSE:
- * Get the arena pointer
- *
- * RETURNS:
- * Arena pointer
- */
-
- void * dbm_getarena()
- {
- if ( ! dbm_arena ) {
- dbm_arena = acreate(
- sbrk( dbm_extend_size ),
- dbm_extend_size,
- 0,
- 0,
- sbrk
- );
- }
- return dbm_arena;
- }
-
- /* ======== db_alloc_chunk ============================================ */
- /* PURPOSE:
- * Allocate a large chunk and free it - This is so that it gets
- * placed in process space so debuggers don't complain.
- *
- * RETURNS:
- * NOthing.
- */
-
- #define DB_CHUNKSIZE 0x40000 /* allocate 256 K */
-
- db_alloc_chunk()
- {
- char * mem;
-
- mem = malloc( DB_CHUNKSIZE );
-
- if ( mem ) {
- free( mem );
- }
-
- return;
-
- } /* end db_alloc_chunk */
-
-
- /* ======== db_printem ================================================ */
- /* PURPOSE:
- * Print some stuff before we go down - just in case we dont get core
- *
- * RETURNS:
- * yes it does!
- */
-
- void db_printem( char * where, int line )
- {
-
- printf( "%s:%d found heap trashing\n", __FILE__, line );
- printf( " diagnotic : %s\n", where );
- printf( "db_bugptr1 = 0x%08x\n", db_bugptr1 );
- printf( "db_bugptr2 = 0x%08x\n", db_bugptr2 );
- printf( "db_bugptr3 = 0x%08x\n", db_bugptr3 );
- printf( "db_bugptr4 = 0x%08x\n", db_bugptr4 );
- printf( "->>>dumping core\n" );
- fflush( stdout );
-
- return;
-
- } /* end db_printem */
-
-
- /* ======== db_check ================================================== */
- /* PURPOSE:
- * Check the arena for failure.
- *
- * RETURNS:
- * Nothing when ok, does not return on failure.
- */
-
- db_check( int doitanyway )
- {
- db_malsort * ptr;
- char * getenv();
- char * str;
-
- /* Check to see if debugger is reqired. */
- if ( db_check_on == -1 ) {
-
- if ( str = getenv( "DB_CHECK" ) ) {
- db_check_on = ! strcmp( str, "ON" );
- } else {
- db_check_on = 0;
- }
-
- if ( str = getenv( "DB_CHECK_SIZE" ) ) {
- db_sizeit = atoi( str );
- }
-
- db_alloc_chunk();
- }
-
- if ( db_check_on || doitanyway ) {
- /* Do memory arena consistency check. */
-
- ptr = db_headlink;
- db_lastgood = 0;
-
- do {
-
- /* if written before allocated memory */
- if ( ptr->db_size != DB_ALIGN( ptr->db_rsize ) ) {
- db_bugptr1 = ptr;
- db_bugptr3 = ( long * ) &( ptr->db_rsize );
- db_bugptr4 = ( long * ) &( ptr->db_size );
- db_printem( "(before allocated mem 1)", __LINE__ );
- * db_coredump = 0; /* Core dump! */
- exit( 4 );
- }
-
- /* if writing over the end of allocated memory... */
- if (
- * ( long * )
- ( ( char * ) ptr + ( ptr->db_size ) + sizeof( * ptr ) ) !=
- DB_IDVAL
- ) {
- db_bugptr3 = ( long * )
- ( ( char * ) ptr + ( ptr->db_size ) + sizeof( * ptr ) )
- ;
- db_bugptr1 = ptr;
- db_printem( "(after alloacted mem 2)", __LINE__ );
- * db_coredump = 0; /* Core dump! */
- exit( 4 );
- }
-
- /* Test the ID */
- if ( ptr->db_id != DB_IDVAL ) {
- db_bugptr3 = ( long * ) & ( ptr->db_id );
- db_bugptr1 = ptr;
- db_printem( "(unsure - probably before 3)", __LINE__ );
- * db_coredump = 0; /* Core dump! */
- exit( 1 );
- }
-
- db_bugptr2 = ptr->db_links[ 0 ];
- /* Test the back and foreward links */
- if ( ptr->db_links[ 0 ]->db_links[ 1 ] != ptr ) {
- db_bugptr1 = ptr;
- db_bugptr2 = ptr->db_links[ 0 ];
- db_bugptr3 = ( long * ) &( ptr->db_links[ 0 ] );
- db_bugptr4 = ( long * ) &( ptr->db_links[ 0 ]->db_links[ 1 ] );
- db_printem( "(unsure - forward/back link wrong 4)", __LINE__ );
- * db_coredump = 0; /* Core dump! */
- exit( 2 );
- }
-
- db_bugptr2 = ptr->db_links[ 1 ];
- /* Test the forward and back links. */
- if ( ptr->db_links[ 1 ]->db_links[ 0 ] != ptr ) {
- db_bugptr1 = ptr;
- db_bugptr2 = ptr->db_links[ 1 ];
- db_bugptr3 = ( long * ) &( ptr->db_links[ 1 ] );
- db_bugptr4 = ( long * ) &( ptr->db_links[ 1 ]->db_links[ 0 ] );
- db_printem( "(unsure - back/forward link wrong 5)", __LINE__ );
- * db_coredump = 0; /* Core dump! */
- exit( 2 );
- }
- db_bugptr2 = 0;
-
- db_lastgood = ptr;
- ptr = ptr->db_links[ 1 ];
-
- } while ( ptr != db_headlink );
- }
-
- return;
-
- } /* end db_check */
-
- /* ======== db_break ================================================== */
- /* PURPOSE:
- * Break point address when memory of size >= db_sizeit requested.
- * Provides an interesting breakpoint address.
- *
- * RETURNS:
- * Nothing.
- */
-
- void db_break( ptr )
- void * ptr;
- {
-
- /* memory of size >= db_sizeit requested. Do something interesting */
- return;
-
- } /* end db_break */
-
-
- /* ======== malloc ================================================= */
- /* PURPOSE:
- * malloc entry point for malloc debugger.
- *
- * RETURNS:
- * Pointer to allocated memory.
- */
-
- void * malloc( size_t osize )
- {
- db_malsort * ptr;
- int size;
-
- size = DB_ALIGN( osize );
-
- db_check( 0 );
-
- ptr = ( db_malsort * ) amalloc(
- size + sizeof( * ptr ) + sizeof( long ),
- dbm_getarena()
- );
-
- if ( ptr ) {
- ptr->db_rsize = osize;
- ptr->db_size = size;
-
- db_linkit( ptr );
-
- if ( size >= db_sizeit ) {
- db_break(
- (long *) ( ( (char *) ptr ) + (ptr->db_size) + sizeof(* ptr) )
- );
- }
-
- return ( char * ) ( ptr + 1 );
- } else {
-
- return ( char * ) 0;
- }
-
- } /* end malloc */
-
-
- /* ======== realloc ================================================ */
- /* PURPOSE:
- * Reallocate
- *
- * RETURNS:
- *
- */
-
- void * realloc( void * vptr, size_t osize )
- {
- db_malsort * ptr = vptr;
- int size;
- db_check( 0 );
-
- size = DB_ALIGN( osize );
-
- ptr --;
- db_unlink( ptr );
-
- ptr = ( db_malsort * )
- arealloc( ptr, size + sizeof( * ptr ) + sizeof( long ), dbm_getarena() )
- ;
-
- if ( ptr ) {
- ptr->db_size = size;
- ptr->db_rsize = osize;
-
- db_linkit( ptr );
-
- return ( char * ) ( ptr + 1 );
- } else {
- return ( char * ) 0;
- }
-
- } /* end realloc */
-
-
- /* ======== free =================================================== */
- /* PURPOSE:
- * Free entry point.
- *
- * RETURNS:
- * Nothing.
- */
-
- void free( void * vptr )
- {
- db_malsort * ptr = ( db_malsort * ) vptr;
- ptr -= 1;
-
- db_check( 0 );
-
- db_unlink( ptr );
-
- afree( ptr, dbm_getarena() );
-
- return;
-
- } /* end free */
-
-
- /* ======== db_unlink ================================================= */
- /* PURPOSE:
- * Unlink pointer from the list.
- *
- * RETURNS:
- *
- */
-
- db_unlink( ptr )
- db_malsort * ptr;
- {
-
- ptr->db_links[ 0 ]->db_links[ 1 ] = ptr->db_links[ 1 ];
- ptr->db_links[ 1 ]->db_links[ 0 ] = ptr->db_links[ 0 ];
-
- return;
-
- } /* end db_unlink */
-
-
- /* ======== db_linkit ================================================= */
- /* PURPOSE:
- * link a pointer into the list.
- *
- * RETURNS:
- *
- */
-
- db_linkit( ptr )
- db_malsort * ptr;
- {
-
- /* Place the ID */
- ptr->db_id = DB_IDVAL;
-
- ptr->db_links[ 0 ] = db_headlink->db_links[ 0 ];
- ptr->db_links[ 1 ] = db_headlink;
-
- db_headlink->db_links[ 0 ] = ptr;
- ptr->db_links[ 0 ]->db_links[ 1 ] = ptr;
-
- * ( long * ) ( ( char * ) ptr + ( ptr->db_size ) + sizeof( * ptr ) ) =
- DB_IDVAL
- ;
-
- return;
-
- } /* end db_linkit */
-
-
- /* ======== calloc ================================================= */
- /* PURPOSE:
- * Calloc debugger entry point
- *
- * RETURNS:
- *
- */
-
- void * calloc( size_t nelem, size_t size )
- {
- char * p;
- int len;
-
- p = malloc( len = nelem * size );
-
- if ( p ) {
- memset( p, 0, len );
- }
-
- return p;
-
- } /* end calloc */
-
-
- int mallopt( int1, int2 )
- int int1;
- int int2;
- {
- return amallopt( int1, int2, dbm_getarena() );
- }
-
- struct mallinfo mallinfo()
- {
- return amallinfo( dbm_getarena() );
- }
-
- #if TEST
- int i;
-
- main()
- {
-
- char * memory[ 100 ];
-
- for ( i = 0; i < 100; i ++ ) {
-
- memory[ i ] = malloc( i );
-
- db_check_on = 1;
- }
-
- for ( i = 0; i < 50; i++ ) {
-
- free( memory[ i * 2 ] );
-
- }
-
- printf( "OK so far - lets detect one ! %x !\n", ( memory[ 49 ] - 2 ) );
-
- * ( memory[ 49 ] - 2 ) = 0;
- * ( memory[ 49 ] - 10 ) = 0;
-
- for ( i = 0; i < 50; i++ ) {
-
- printf( "%d\n", i );
- free( memory[ 1 + i * 2 ] );
-
- }
-
- }
-
- #endif
-